﻿using Emgu.CV;
using Emgu.CV.Structure;
using FeiYang;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using WindowsInput.Native;

namespace MaiYao
{
    public enum 包裹位置_
    {
        无_,
        正常_,
        上方_,
    }

    public enum 移动方向_
    {
        无_,
        左_,
        左上_,
        上_,
        右上_,
        右_,
        右下_,
        下_,
        左下_
    }

    public class 坐标_
    {
        public 坐标_(int X, int Y)
        {
            this.X = X;
            this.Y = Y;
        }

        public static 坐标_ 格式化坐标(string 内容)
        {
            try
            {
                var n1 = Regex.Replace(Regex.Replace(内容.Trim(), @"[\u4e00-\u9fa5]", ""), "[;；，,:：]", ":");
                var s = n1.Split(':');
                return new 坐标_(int.Parse(s[0].Trim()), int.Parse(s[1].Trim()));
            }
            catch
            {
                return 零值();
            }
        }

        public static 坐标_ 零值()
        {
            return new 坐标_(int.MaxValue, int.MaxValue);
        }

        public int X { get; set; }
        public int Y { get; set; }
        public override string ToString()
        {
            return "X : " + this.X.ToString() + ", " + "Y : " + this.Y.ToString();
        }

        public override bool Equals(object obj)
        {
            return obj is 坐标_ v &&
                   X == v.X &&
                   Y == v.Y;
        }
        public static bool operator ==(坐标_ L, 坐标_ R)
        {
            return L.Equals(R);
        }

        public static bool operator !=(坐标_ L, 坐标_ R)
        {
            return !L.Equals(R);
        }

        public static explicit operator Point(坐标_ 坐标)
        {
            return new Point(坐标.X, 坐标.Y);
        }


        public static 移动方向_ 判断移动方向(坐标_ 角色位置, 坐标_ 目标位置)
        {
            var X = 角色位置.X - 目标位置.X;
            var Y = 角色位置.Y - 目标位置.Y;
            if (X == 0)
            {
                if (Y == 0)
                {
                    return 移动方向_.无_;
                }
                else if (Y > 0)
                {
                    return 移动方向_.上_;
                }
                else
                {
                    return 移动方向_.下_;
                }
            }
            else if (X > 0)
            {
                if (Y == 0)
                {
                    return 移动方向_.左_;
                }
                else if (Y > 0)
                {
                    return 移动方向_.左上_;
                }
                else
                {
                    return 移动方向_.左下_;
                }
            }
            else
            {
                if (Y == 0)
                {
                    return 移动方向_.右_;
                }
                else if (Y > 0)
                {
                    return 移动方向_.右上_;
                }
                else
                {
                    return 移动方向_.右下_;
                }
            }
        }
    }

    public class 点位转换
    {
        public static Point 从右上向左下找点(Rectangle rect, int 向左距离, int 向下距离)
        {
            return new Point(rect.Width - 向左距离, 向下距离);
        }

        public static Point 从右下向左上找点(Rectangle rect, int 向左距离, int 向上距离)
        {
            return new Point(rect.Width - 向左距离, rect.Height - 向上距离);
        }

        public static Point 从左上向右下找点(Rectangle rect, int 向右距离, int 向下距离)
        {
            return new Point(rect.Left + 向右距离, rect.Top - 向下距离);
        }

        public static Point 从左下向右上找点(Rectangle rect, int 向右距离, int 向上距离)
        {
            return new Point(rect.Left + 向右距离, rect.Height - 向上距离);
        }

        public static Point 从中间向左上找点(Rectangle rect, int 向左距离, int 向上距离)
        {
            return new Point(rect.Width / 2 - 向左距离, rect.Height / 2 - 向上距离);
        }

        public static Point 从中间底部向右上找点(Rectangle rect, int 向右距离, int 向上距离)
        {
            return new Point(rect.Width / 2 + 向右距离, rect.Height - 向上距离);
        }

        public static Point 从中间底部向左上找点(Rectangle rect, int 向左距离, int 向上距离)
        {
            return new Point(rect.Width / 2 - 向左距离, rect.Height - 向上距离);
        }

        public static Point 获取屏幕坐标(挂机人物 人物, int x, int y)
        {
            return new Point(人物.窗口零点坐标.X + x, 人物.窗口零点坐标.Y + y);
        }
    }

    public class 操作
    {

        public static string 读取最近一条聊天(挂机人物 人物)
        {
            return 人物.读内存.读取字符串("00050b3c->20");
        }

        public static string 获取当前地图名(挂机人物 人物)
        {
            return 人物.读内存.读取字符串("006064D8->0");
        }

        public static 坐标_ 获取当前鼠标坐标(挂机人物 人物)
        {
            var x = 人物.读内存.读取32位整数("00641AB8->22");
            var y = 人物.读内存.读取32位整数("00641AB8->26");
            return new 坐标_(x, y);
        }

        public static 坐标_ 获取当前坐标(挂机人物 人物)
        {
            var x = 人物.读内存.读取32位整数("00641AB8->14");
            var y = 人物.读内存.读取32位整数("00641AB8->18");
            return new 坐标_(x, y);
        }

        public static Rectangle 获取UI范围(挂机人物 人物)
        {
            var x = 人物.读内存.读取32位整数("006064D4->12C->28");
            var y = 人物.读内存.读取32位整数("006064D4->12C->32");
            var w = 人物.读内存.读取32位整数("006064D4->12C->36");
            var h = 人物.读内存.读取32位整数("006064D4->12C->40");
            return new Rectangle(x, y, w, h);
        }

        public static Point 包裹位差 = new Point(33, -168);

        public static int 临时计数器 = 100;

        public static int 识别金币数量(挂机人物 人物)
        {
            包裹位置_ 位置 = 包裹位置_.无_;
            if (!其他_.循环判断(() =>
            {
                位置 = 确认打开包裹(人物);
                return 位置 != 包裹位置_.无_;
            }, 2, 400))
            {
                return 0;
            }
            var 开始 = DateTime.Now;
            var x = 位置 == 包裹位置_.上方_ ? 528 : 528 + 包裹位差.X;
            var y = 位置 == 包裹位置_.上方_ ? 363 : 363 - 包裹位差.Y;
            var x2 = 位置 == 包裹位置_.上方_ ? 611 : 611 + 包裹位差.X;
            var y2 = 位置 == 包裹位置_.上方_ ? 371 : 371 - 包裹位差.Y;
            var p1 = 点位转换.获取屏幕坐标(人物, x, y);
            var p2 = 点位转换.获取屏幕坐标(人物, x2, y2);
            var r = 识别包裹金币();
            人物.输出("金币识别: " + r + " 耗时: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
            return r;

            int 识别包裹金币()
            {
                using (var 包裹金币文字 = 图像.截图(p1, p2))
                using (var 包裹金币文字Imgae = 包裹金币文字.ToImage<Bgr, byte>())
                {
                    var 值分散组 = 图像.分块模板匹配(包裹金币文字Imgae);
                    return 内部转换(值分散组);
                }

                int 内部转换(List<int> 数值组)
                {
                    var 结果 = 0;
                    foreach (var n in 数值组)
                    {
                        if (n < 10)
                        {
                            结果 = 结果 * 10 + n;
                        }
                        else
                        {
                            return -1;
                        }
                    }
                    return 结果;
                }
            }
        }

        public static int 识别仓库库存(挂机人物 人物)
        {
            var 开始 = DateTime.Now;
            using (var 仓库数量图像 = 图像.截图(点位转换.获取屏幕坐标(人物, 47, 126), 点位转换.获取屏幕坐标(人物, 77, 134)))
            using (var 仓库数量图像Imgae = 仓库数量图像.ToImage<Bgr, byte>())
            {
                var 值分散组 = 图像.分块模板匹配(仓库数量图像Imgae);
                var R = 内部转换(值分散组);
                人物.输出("识别库存: " + R + " 耗时: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
                return R;
            }

            int 内部转换(List<int> 数值组)
            {
                var 结果 = 0;
                foreach (var n in 数值组)
                {
                    if (n < 10)
                    {
                        结果 = 结果 * 10 + n;
                    }
                }
                return 结果;
            }
        }

        public static 包裹位置_ 确认打开包裹(挂机人物 人物)
        {
            var 开始 = DateTime.Now;
            var px = 点位转换.获取屏幕坐标(人物, 691 - 包裹位差.X, 59 - 包裹位差.Y);
            var py = 点位转换.获取屏幕坐标(人物, 717 - 包裹位差.X, 77 - 包裹位差.Y);
            using (var 包裹 = 图像.截图(px, py))
            using (var img = 包裹.ToImage<Bgr, byte>())
            using (var img2 = Properties.Resources.包裹.ToImage<Bgr, byte>())
            {
                var R = 图像.模板匹配(img, img2);
                if (R > 0.9)
                {
                    人物.输出("确认包裹已经打开: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
                    return 包裹位置_.正常_;
                }
                var p3 = 点位转换.获取屏幕坐标(人物, 691, 59);
                var p4 = 点位转换.获取屏幕坐标(人物, 717, 77);
                using (var 包裹2 = 图像.截图(p3, p4))
                using (var img3 = 包裹2.ToImage<Bgr, byte>())
                {
                    var R2 = 图像.模板匹配(img3, img2);
                    if (R2 > 0.9)
                    {
                        人物.输出("确认包裹已经打开: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
                        return 包裹位置_.上方_;
                    }
                    保存图像(包裹, "错误包裹");
                    保存图像(包裹2, "错误包裹");
                    人物.输出("包裹未打开: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
                    return 包裹位置_.无_;
                }
            }
        }

        public static 包裹位置_ 打开包裹(挂机人物 人物)
        {
            键鼠_.按下(VirtualKeyCode.F9);
            其他_.延迟(其他_.时长_600);
            return 确认打开包裹(人物);
        }

        public static void 切换到第二页(挂机人物 人物)
        {
            var p1 = 点位转换.获取屏幕坐标(人物, 828, 247);
            键鼠_.左键单击(p1);
            其他_.延迟(其他_.时长_600);
        }

        public static void 整理包裹(挂机人物 人物, 包裹位置_ 位置)
        {
            var x = 位置 == 包裹位置_.正常_ ? 696 : 696 + 包裹位差.X;
            var y = 位置 == 包裹位置_.正常_ ? 569 : 569 + 包裹位差.Y;
            var p1 = 点位转换.获取屏幕坐标(人物, x, y);
            键鼠_.左键单击(p1);
            其他_.延迟(其他_.时长_600);
        }

        public static void 遍历一也包裹(挂机人物 人物, 包裹位置_ 位置, 包裹回调_ fn)
        {
            var x1 = 位置 == 包裹位置_.正常_ ? 467 : 467 + 包裹位差.X;
            var y1 = 位置 == 包裹位置_.正常_ ? 285 : 285 + 包裹位差.Y;
            var p1 = 点位转换.获取屏幕坐标(人物, x1, y1);
            for (var y = 0; y <= 5; y++)
            {
                for (var x = 0; x <= 9; x++)
                {
                    var p = new Point(p1.X + x * 39, p1.Y + ((int)(y * 38.5)));
                    var r = fn(p);
                    if (!r) return;
                }
            }
            return;
        }

        public static void 遍历一页包裹扔药(挂机人物 人物, 包裹位置_ 位置)
        {
            var x1 = 位置 == 包裹位置_.正常_ ? 357 : 956;
            var y1 = 位置 == 包裹位置_.正常_ ? 364 : 297;
            var 扔药点 = 点位转换.获取屏幕坐标(人物, x1, y1);
            var 延迟时长 = 人物.人物列表.Count > 1 ? 其他_.时长_600 : 其他_.时长_200;
            遍历一也包裹(人物, 位置, p =>
            {
                var p1 = new Point(p.X - 25, p.Y - 25);
                var p2 = new Point(p.X + 25, p.Y + 25);
                using (var bmp = 图像.截图(p1.X, p1.Y, p2.X, p2.Y))
                {
                    if (图像.大图中有小图(bmp, Properties.Resources.治疗药水, 0.6))
                    {
                        键鼠_.左键单击(p);
                        其他_.延迟(其他_.时长_100);
                        键鼠_.左键单击(扔药点);
                        其他_.延迟(延迟时长);
                        return !人物.cts.IsCancellationRequested;
                    }
                    else if (图像.大图中有小图(bmp, Properties.Resources.空包裹, 0.7) || 图像.大图中有小图(bmp, Properties.Resources.锁定包裹, 0.7))
                    {
                        键鼠_.移动鼠标(p);
                        其他_.延迟(其他_.时长_1000);
                        return false;
                    }
                    return !人物.cts.IsCancellationRequested;
                }
            });
        }

        public static 任务名_ 扔一包裹药(挂机人物 人物)
        {
            其他_.延迟(其他_.时长_400);
            var 位置 = 打开包裹(人物);
            if (位置 == 包裹位置_.无_)
            {
                人物.回海岛休息时长 = 0;
                人物.回岛原因 = "没有发现包裹标志";
                return 任务名_.回海岛休息;
            }
            for (var i = 0; i < 2 && !人物.cts.IsCancellationRequested; i++)
            {
                整理包裹(人物, 位置);
                遍历一页包裹扔药(人物, 位置);
                其他_.延迟(其他_.时长_400);
            }
            Esc关闭窗口();
            其他_.延迟(其他_.时长_400);
            Esc关闭窗口();
            其他_.延迟(其他_.时长_400);
            return 任务名_.仓库取药;
        }

        public static Point 计算鼠标坐标与目标坐标差距(坐标_ 鼠标坐标, 坐标_ 目标坐标, Point 屏幕像素)
        {
            var 方向 = 坐标_.判断移动方向(鼠标坐标, 目标坐标);
            var x = Math.Abs(鼠标坐标.X - 目标坐标.X);
            var y = Math.Abs(鼠标坐标.Y - 目标坐标.Y);
            x *= 坐标单位.X;
            y *= 坐标单位.Y;

            switch (方向)
            {
                case 移动方向_.左_:
                    return new Point(屏幕像素.X - x, 屏幕像素.Y);
                case 移动方向_.左上_:
                    return new Point(屏幕像素.X - x, 屏幕像素.Y - y);
                case 移动方向_.上_:
                    return new Point(屏幕像素.X, 屏幕像素.Y - y);
                case 移动方向_.右上_:
                    return new Point(屏幕像素.X + x, 屏幕像素.Y - y);
                case 移动方向_.右_:
                    return new Point(屏幕像素.X + x, 屏幕像素.Y);
                case 移动方向_.右下_:
                    return new Point(屏幕像素.X + x, 屏幕像素.Y + y);
                case 移动方向_.下_:
                    return new Point(屏幕像素.X, 屏幕像素.Y + y);
                case 移动方向_.左下_:
                    return new Point(屏幕像素.X - x, 屏幕像素.Y + y);
                default:
                    return new Point(屏幕像素.X, 屏幕像素.Y);
            }
        }

        public static Point 人物原点 = new Point(672, 400);
        public static Point 坐标单位 = new Point(64, 32);

        public static Point 根据人物坐标计算目标坐标屏幕点位(挂机人物 人物, 坐标_ 人物坐标, 坐标_ 目标坐标)
        {
            var 方向 = 坐标_.判断移动方向(人物坐标, 目标坐标);
            var x = Math.Abs(人物坐标.X - 目标坐标.X);
            var y = Math.Abs(人物坐标.Y - 目标坐标.Y);
            x *= 坐标单位.X;
            y *= 坐标单位.Y;
            switch (方向)
            {
                case 移动方向_.左_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X - x, 人物原点.Y - 32);
                case 移动方向_.左上_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X - x, 人物原点.Y - 32 - y);
                case 移动方向_.上_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X, 人物原点.Y - 32 - y);
                case 移动方向_.右上_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X + x, 人物原点.Y - 32 - y);
                case 移动方向_.右_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X + x, 人物原点.Y - 32);
                case 移动方向_.右下_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X + x, 人物原点.Y - 32 + y);
                case 移动方向_.下_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X, 人物原点.Y - 32 + y);
                case 移动方向_.左下_:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X - x, 人物原点.Y - 32 + y);
                default:
                    return 点位转换.获取屏幕坐标(人物, 人物原点.X, 人物原点.Y - 32);
            }
        }

        public static bool 确认药店已经打开(挂机人物 人物)
        {
            var 开始 = DateTime.Now;
            var p1 = 点位转换.获取屏幕坐标(人物, 36, 53);
            var p2 = 点位转换.获取屏幕坐标(人物, 190, 67);
            using (var bmp1 = 图像.截图(p1.X, p1.Y, p2.X, p2.Y))
            using (var img1 = bmp1.ToImage<Bgr, byte>())
            using (var img2 = Properties.Resources.欢迎光临这里有各种药品.ToImage<Bgr, byte>())
            {
                var R = 图像.模板匹配(img1, img2);
                人物.输出("确认药店已经打开: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
                return R > 0.9;
            }
        }

        public static 任务名_ 沙城药店买药(挂机人物 人物)
        {
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 68, 95));
            其他_.延迟(其他_.时长_1000);

            var 金币数量 = 识别金币数量(人物);

            人物.输出(金币数量.ToString());

            if (金币数量 < 100000)
            {
                人物.输出("金币不足结束任务");
                if (人物.回海岛休息次数 > 5)
                {
                    人物.回海岛休息时长 = 0;
                }
                人物.回岛原因 = "金币不足";
                return 任务名_.回海岛休息;
            }

            键鼠_.移动鼠标(点位转换.获取屏幕坐标(人物, 363, 291));
            其他_.延迟(其他_.时长_50);

            var 找到 = 找到治疗药水();
            if (!找到)
            {
                人物.输出("没发现治疗药水");
                人物.回岛原因 = "没发现治疗药水";
                人物.应恢复任务 = 任务名_.去海岛存药;
                return 任务名_.回海岛休息;
            }

            var 确定按钮 = 点位转换.获取屏幕坐标(人物, 134, 463);
            键鼠_.移动鼠标(确定按钮);
            其他_.延迟(其他_.时长_400);

            for (var i = 0; i < 人物.角色包裹空格数量 * 2 && !人物.cts.IsCancellationRequested; i++)
            {
                其他_.延迟(30);
                键鼠_.左键按下();
                键鼠_.左键抬起();
            }

            var 坐标 = 点位转换.获取屏幕坐标(人物, 671, 341);
            其他_.延迟(其他_.时长_100);
            键鼠_.左键单击(坐标);
            其他_.延迟(其他_.时长_100);
            Esc关闭窗口();
            其他_.延迟(其他_.时长_100);
            键鼠_.左键单击(坐标);
            其他_.延迟(其他_.时长_100);
            Esc关闭窗口();
            return 任务名_.去海岛存药;

            bool 找到治疗药水()
            {
                键鼠_.移动鼠标(点位转换.获取屏幕坐标(人物, 363, 291));
                其他_.延迟(其他_.时长_100);
                键鼠_.左键按下();
                其他_.延迟(其他_.时长_50);
                键鼠_.移动鼠标(点位转换.获取屏幕坐标(人物, 363, 354));
                其他_.延迟(其他_.时长_50);
                键鼠_.左键抬起();
                其他_.延迟(其他_.时长_400);
                var 治疗药水位置 = 查找治疗药水位置();
                if (治疗药水位置 != -1)
                {
                    var p1 = 点位转换.获取屏幕坐标(人物, 100, 治疗药水位置);
                    键鼠_.左键单击(p1);
                    return true;
                }

                键鼠_.左键按下();
                其他_.延迟(其他_.时长_50);
                键鼠_.移动鼠标(点位转换.获取屏幕坐标(人物, 363, 389));
                其他_.延迟(其他_.时长_50);
                键鼠_.左键抬起();
                其他_.延迟(其他_.时长_400);

                治疗药水位置 = 查找治疗药水位置();
                if (治疗药水位置 != -1)
                {
                    var p1 = 点位转换.获取屏幕坐标(人物, 100, 治疗药水位置);
                    键鼠_.左键单击(p1);
                    return true;
                }
                return false;

                int 查找治疗药水位置()
                {
                    var p1 = 点位转换.获取屏幕坐标(人物, 68, 266);
                    var p2 = 点位转换.获取屏幕坐标(人物, 117, 446);
                    using (var a = 图像.截图(p1.X, p1.Y, p2.X, p2.Y))
                    using (var img2 = Properties.Resources.治疗药水文字.ToImage<Bgr, byte>())
                    {
                        var list = 图像.分割为行(a, 18);
                        var 位置 = -1;
                        for (var i = 0; i < list.Count; i++)
                        {
                            using (var img = list[i])
                            {
                                var R = 图像.模板匹配(img, img2);
                                if (R > 0.9)
                                {
                                    位置 = 266 + 9 + i * 18;
                                    break;
                                }
                            }
                            list[i] = null;
                        }
                        list.Clear();
                        return 位置;
                    }
                }
            }
        }

        public static bool 确认药水仓库已打开(挂机人物 人物)
        {
            var 开始 = DateTime.Now;
            var p1 = 点位转换.获取屏幕坐标(人物, 32, 52);
            var p2 = 点位转换.获取屏幕坐标(人物, 223, 70);
            using (var bmp1 = 图像.截图(p1.X, p1.Y, p2.X, p2.Y))
            using (var img1 = bmp1.ToImage<Bgr, byte>())
            using (var img2 = Properties.Resources.你可以在这里存放和取出指定药水.ToImage<Bgr, byte>())
            {
                var R = 图像.模板匹配(img1, img2);
                人物.输出("识别打开仓库: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
                return R > 0.9;
            }
        }

        public static bool 确认是否有二级码设置提示(挂机人物 人物)
        {
            var 开始 = DateTime.Now;
            var p1 = 点位转换.获取屏幕坐标(人物, 32, 52);
            var p2 = 点位转换.获取屏幕坐标(人物, 302, 70);
            using (var bmp1 = 图像.截图(p1.X, p1.Y, p2.X, p2.Y))
            using (var img1 = bmp1.ToImage<Bgr, byte>())
            using (var img2 = Properties.Resources.经系统鉴定您的账号比较珍贵建议您立即设置.ToImage<Bgr, byte>())
            {
                var R = 图像.模板匹配(img1, img2);
                人物.输出("识别是否有密码: " + (DateTime.Now - 开始).TotalMilliseconds.ToString("f0") + "毫秒");
                return R > 0.9;
            }
        }

        public static void 打开小地图(挂机人物 人物)
        {
            键鼠_.按下(VirtualKeyCode.CONTROL);
            其他_.延迟(10);
            键鼠_.按下(VirtualKeyCode.TAB);
            其他_.延迟(100);
            键鼠_.松开(VirtualKeyCode.CONTROL);
            其他_.延迟(10);
            键鼠_.松开(VirtualKeyCode.TAB);
            其他_.延迟(其他_.时长_600);
        }

        public static void 打开今日挑战(挂机人物 人物)
        {
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 1034, 133));
            其他_.延迟(其他_.时长_400);
        }

        public static void Esc关闭窗口()
        {
            键鼠_.按下(VirtualKeyCode.ESCAPE);
        }

        public static 任务名_ 去沙城药店(挂机人物 人物)
        {
            var 计次 = 0;
        _开始_:
            打开今日挑战(人物);
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 204, 366));
            其他_.延迟(其他_.时长_400);
            Esc关闭窗口();
            其他_.延迟(其他_.时长_100);

            键鼠_.移动鼠标(点位转换.获取屏幕坐标(人物, 人物原点.X, 人物原点.Y + 64));
            其他_.延迟(10);
            键鼠_.右键按下();
            坐标_ 人物坐标; ;
            坐标_ 城主雕像坐标 = new 坐标_(227, 871);
            bool 条件;
            var i = 0;
            do
            {
                其他_.延迟(其他_.时长_1000);
                人物坐标 = 操作.获取当前坐标(人物);
                条件 = Math.Abs(人物坐标.Y - 城主雕像坐标.Y) <= 4;
                i++;
            }
            while (!条件 && i < 30);
            键鼠_.右键抬起();
            其他_.延迟(500);
            if (i > 26)
            {
                //回海岛休息
                人物.回岛原因 = "没找到城主雕像";
                人物.应恢复任务 = 任务名_.去海岛存药;
                return 任务名_.回海岛休息;
            }

            // 停止行走
            键鼠_.右键单击(点位转换.获取屏幕坐标(人物, 673, 400));
            其他_.延迟(其他_.时长_400);
            var 计次2 = 0;

        重试坐标_:
            人物坐标 = 操作.获取当前坐标(人物);
            if (Math.Abs(人物坐标.X - 城主雕像坐标.X) > 6)
            {
                计次++;
                键鼠_.右键单击(点位转换.获取屏幕坐标(人物, 人物原点.X - 120, 人物原点.Y));
                其他_.延迟(其他_.时长_1000);
                if (计次 > 2)
                {
                    goto _开始_;
                }
                goto 重试坐标_;
            }
            var p = 根据人物坐标计算目标坐标屏幕点位(人物, 人物坐标, 城主雕像坐标);

            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(p.X, p.Y);
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 242, 156));
            其他_.延迟(其他_.时长_1000);
            人物坐标 = 操作.获取当前坐标(人物);
            if (Math.Abs(人物坐标.X - 212) > 6 || Math.Abs(人物坐标.Y - 425) > 6)
            {
                计次++;
                if (计次 > 4)
                {
                    //回海岛休息
                    人物.应恢复任务 = 任务名_.去海岛存药;
                    人物.回岛原因 = "没能传送到沙城门口";
                    return 任务名_.回海岛休息;
                }
                if (计次2 < 3)
                {
                    计次2++;
                    人物.输出("点击城主雕像失败重试3次");
                    goto 重试坐标_;
                }
                goto _开始_;
            }
            人物.输出("已到达沙城门口");
            打开小地图(人物);
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 465, 390));
            其他_.延迟(其他_.时长_400);
            Esc关闭窗口();

            键鼠_.移动鼠标(点位转换.获取屏幕坐标(人物, 673, 400));

            var 药店坐标 = new 坐标_(213, 388);

            bool 条件2;
            i = 0;
            do
            {
                其他_.延迟(其他_.时长_2000);
                人物坐标 = 操作.获取当前坐标(人物);
                条件2 = Math.Abs(人物坐标.X - 药店坐标.X) <= 5 && Math.Abs(人物坐标.Y - 药店坐标.Y) <= 5;
                i++;
            }
            while (!条件2 && i < 20);
            if (i > 15)
            {
                人物.应恢复任务 = 任务名_.去海岛存药;
                人物.回岛原因 = "没能走到药店";
                return 任务名_.回海岛休息;
            }
            // 停止行走
            键鼠_.右键单击();
            其他_.延迟(其他_.时长_100);
            人物坐标 = 操作.获取当前坐标(人物);
            var p2 = 根据人物坐标计算目标坐标屏幕点位(人物, 人物坐标, 药店坐标);
            人物.输出("已到达沙城药店");
            其他_.延迟(其他_.时长_200);
            键鼠_.左键单击(p2.X, p2.Y);
            return 任务名_.开始买药;
        }

        public static void 去海岛仓库(挂机人物 人物)
        {
            打开今日挑战(人物);
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 203, 471));
            其他_.延迟(其他_.时长_1000);
            Esc关闭窗口();
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 1033, 543));
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 57, 144));
            其他_.延迟(其他_.时长_1000);
        }

        public static 任务名_ 打开仓库取药(挂机人物 人物)
        {
            人物.本包已经扔完 = false;
            var 捡药包裹空格 = 人物.角色包裹空格数量;
            if (人物.人物列表.Count > 1)
            {
                var 捡药的 = 人物.人物列表.Find(r => r.挂机项目 == 挂机项目_.捡药);
                捡药包裹空格 = 捡药的.角色包裹空格数量;
                if (捡药的.已经捡满了)
                {
                    人物.输出("捡药的已经满包, 停止挂机");
                    return 任务名_.无;
                }
                if (人物.仓库管理员坐标 != 捡药的.仓库管理员坐标)
                {
                    人物.输出("捡药的管理员坐标和扔药的不同, 停止挂机");
                    return 任务名_.无;
                }
            }
            var 人物坐标 = 操作.获取当前坐标(人物);
            var p = 人物坐标 != 坐标_.零值() ?
                根据人物坐标计算目标坐标屏幕点位(人物, 人物坐标, 人物.仓库管理员坐标) :
                图像.在范围内找模板返回中心(点位转换.获取屏幕坐标(人物, 0, 0), 点位转换.获取屏幕坐标(人物, 1280, 800), Properties.Resources.掌柜的, 0.3);
            if (人物坐标 == 坐标_.零值())
            {
                人物.输出("未能准确识别人物坐标");
                return 任务名_.无;
            }

            键鼠_.左键单击(p.X, p.Y);
            其他_.延迟(其他_.时长_600);

            if (其他_.循环判断(() => 确认是否有二级码设置提示(人物), 2, 400))
            {
                // 点击 下次再说
                键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 142, 100));
                其他_.延迟(其他_.时长_400);
            }
            // 点击药水仓库
            var 药水仓库按钮 = 点位转换.获取屏幕坐标(人物, 54, 126);
            键鼠_.左键单击(药水仓库按钮);

            if (!其他_.循环判断(() => 确认药水仓库已打开(人物), 2, 400))
            {
                人物.输出("重试2次未能打开仓库");
                return 任务名_.无;
            }

            var 库存 = 识别仓库库存(人物);
            if (库存 <= 人物.扔药剩余数量)
            {
                if (库存 == -1)
                {
                    人物.输出("库存识别错误.");
                }
                else
                {
                    人物.输出("仓库剩余少于10个");
                }
                人物.药已经取完 = true;
                return 任务名_.无;
            }

            // 点击 取药
            var 数量 = Math.Min(人物.角色包裹空格数量, 库存);
            数量 = Math.Min(捡药包裹空格, 数量);
            var 总数 = 0;
            var 取出按钮位置 = 点位转换.获取屏幕坐标(人物, 64, 147);
            var 确定按钮位置 = 点位转换.获取屏幕坐标(人物, 564, 410);

            for (var n = 0; n < 数量 / 50; n++)
            {
                if (n > 0)
                {
                    // 重新打开仓库
                    键鼠_.左键单击(p.X, p.Y);
                    其他_.延迟(其他_.时长_800);
                    if (其他_.循环判断(() => 确认是否有二级码设置提示(人物), 2, 400))
                    {
                        // 点击 下次再说
                        键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 142, 100));
                        其他_.延迟(其他_.时长_400);
                    }
                    // 点击药水仓库
                    键鼠_.左键单击(药水仓库按钮);
                    if (!其他_.循环判断(() => 确认药水仓库已打开(人物), 2, 400))
                    {
                        人物.输出("未能打开仓库暂时跳过");
                        break;
                    }
                }
                键鼠_.左键单击(取出按钮位置);
                其他_.延迟(其他_.时长_800);
                键鼠_.按下(VirtualKeyCode.NUMPAD5);
                其他_.延迟(10);
                键鼠_.松开(VirtualKeyCode.NUMPAD5);
                其他_.延迟(50);
                键鼠_.按下(VirtualKeyCode.NUMPAD0);
                其他_.延迟(10);
                键鼠_.松开(VirtualKeyCode.NUMPAD0);
                总数 += 50;
                其他_.延迟(其他_.时长_400);
                键鼠_.左键单击(确定按钮位置);
                其他_.延迟(其他_.时长_400);
            }
            var 剩余 = 数量 - 总数;

            if (剩余 >= 10)
            {
                键鼠_.左键单击(p.X, p.Y);
                其他_.延迟(其他_.时长_800);
                if (其他_.循环判断(() => 确认是否有二级码设置提示(人物), 2, 400))
                {
                    // 点击 下次再说
                    键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 142, 100));
                    其他_.延迟(其他_.时长_400);
                }
                // 点击药水仓库
                键鼠_.左键单击(药水仓库按钮);
                if (!其他_.循环判断(() => 确认药水仓库已打开(人物), 2, 400))
                {
                    goto 关闭窗口_;
                }

                键鼠_.左键单击(取出按钮位置);

                其他_.延迟(其他_.时长_800);

                var list = 获取代码(剩余.ToString());
                foreach (var c in list)
                {
                    键鼠_.按下(c);
                    其他_.延迟(10);
                    键鼠_.松开(c);
                    其他_.延迟(100);
                }
                其他_.延迟(其他_.时长_400);
                键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 564, 410));
                其他_.延迟(其他_.时长_400);
            }
        关闭窗口_:
            Esc关闭窗口();
            其他_.延迟(其他_.时长_400);
            Esc关闭窗口();
            其他_.延迟(其他_.时长_400);
            var 结果 = 扔一包裹药(人物);
            人物.本包已经扔完 = true;
            return 结果;
        }

        public static List<VirtualKeyCode> 获取代码(string n)
        {
            var list = new List<VirtualKeyCode>();
            foreach (var c in n)
            {
                switch (c)
                {
                    case '1':
                        list.Add(VirtualKeyCode.NUMPAD1);
                        break;
                    case '2':
                        list.Add(VirtualKeyCode.NUMPAD2);
                        break;
                    case '3':
                        list.Add(VirtualKeyCode.NUMPAD3);
                        break;
                    case '4':
                        list.Add(VirtualKeyCode.NUMPAD4);
                        break;
                    case '5':
                        list.Add(VirtualKeyCode.NUMPAD5);
                        break;
                    case '6':
                        list.Add(VirtualKeyCode.NUMPAD6);
                        break;
                    case '7':
                        list.Add(VirtualKeyCode.NUMPAD7);
                        break;
                    case '8':
                        list.Add(VirtualKeyCode.NUMPAD8);
                        break;
                    case '9':
                        list.Add(VirtualKeyCode.NUMPAD9);
                        break;
                    default:
                        list.Add(VirtualKeyCode.NUMPAD0);
                        break;
                }
            }
            return list;
        }

        public static bool 确保打开仓库(挂机人物 人物)
        {
            var 人物坐标 = 操作.获取当前坐标(人物);
            var p = 根据人物坐标计算目标坐标屏幕点位(人物, 人物坐标, 人物.仓库管理员坐标);
            键鼠_.左键单击(p.X, p.Y);
            其他_.延迟(其他_.时长_600);
            return true;
        }

        public static 任务名_ 打开仓库存药(挂机人物 人物)
        {
            确保打开仓库(人物);
            if (其他_.循环判断(() => 确认是否有二级码设置提示(人物), 2, 400))
            {
                // 点击 下次再说
                键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 142, 100));
                其他_.延迟(其他_.时长_400);
            }

            // 点击药水仓库
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 54, 126));

            if (!其他_.循环判断(() => 确认药水仓库已打开(人物), 2, 400))
            {
                // 错误了
                人物.已经捡满了 = true;
                return 任务名_.无;
            }

            // 确认是否要满了;
            var 库存 = 识别仓库库存(人物);
            if ((库存 + 人物.角色包裹空格数量) >= 9900)
            {
                人物.输出("仓库已已满: 库存: " + 库存.ToString());
                人物.已经捡满了 = true;
                return 任务名_.无;
            }

            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 276, 60));
            其他_.延迟(其他_.时长_200);
            if (!其他_.循环判断(() =>
            {
                var p1 = 点位转换.获取屏幕坐标(人物, 32, 52);
                var p2 = 点位转换.获取屏幕坐标(人物, 120, 67);
                using (var bmp = 图像.截图(p1, p2))
                using (var img = bmp.ToImage<Bgr, byte>())
                using (var img2 = Properties.Resources.存药成功.ToImage<Bgr, byte>())
                {
                    var R = 图像.模板匹配(img, img2);
                    if (R > 0.9) return true;
                    using (var img3 = Properties.Resources.存药失败.ToImage<Bgr, byte>())
                    {
                        R = 图像.模板匹配(img, img3);
                        return R > 0.9;
                    }
                }
            }, 2, 400))
            {
                人物.输出("可能出现错误需要重试存药");
                其他_.延迟(其他_.时长_1000);
                return 打开仓库存药(人物);
            }
            人物.输出("成功存入药水");
            其他_.延迟(其他_.时长_200);
            Esc关闭窗口();
            其他_.延迟(其他_.时长_400);
            return 任务名_.准备捡药;
        }

        public static 任务名_ 打开海岛仓库一键存药(挂机人物 人物)
        {
            var 人物坐标 = 操作.获取当前坐标(人物);
            var p = 根据人物坐标计算目标坐标屏幕点位(人物, 人物坐标, 人物.仓库管理员坐标);
            // 点击仓库
            键鼠_.左键单击(p.X, p.Y);
            其他_.延迟(其他_.时长_600);
            if (其他_.循环判断(() => 确认是否有二级码设置提示(人物), 2, 400))
            {
                // 点击 下次再说
                键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 142, 100));
                其他_.延迟(其他_.时长_400);
            }
            // 点击药水仓库
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 54, 126));
            if (!其他_.循环判断(() => 确认药水仓库已打开(人物), 2, 400))
            {
                人物.应恢复任务 = 任务名_.去海岛存药;
                人物.回岛原因 = "没能打开药水仓库";
                return 任务名_.回海岛休息;
            }

            // 确认是否要满了;
            var 库存 = 识别仓库库存(人物);
            if ((库存 + 人物.角色包裹空格数量) >= 9990)
            {
                人物.输出("仓库已有: " + 库存.ToString());
                人物.已经捡满了 = true;
                return 任务名_.无;
            }

            其他_.延迟(其他_.时长_200);
            键鼠_.左键单击(点位转换.获取屏幕坐标(人物, 276, 60));
            其他_.延迟(其他_.时长_400);
            Esc关闭窗口();
            其他_.延迟(其他_.时长_400);
            Esc关闭窗口();
            其他_.延迟(其他_.时长_400);
            return 任务名_.去沙城买药;
        }

        public static void 保存图像(Image<Bgr, byte> img, string 名称 = "")
        {
            using (var b = img.ToBitmap())
            {
                保存图像(b, 名称);
            }
        }

        public static void 保存图像(Bitmap bmp, string 名称 = "")
        {
            var 目录 = 其他_.创建文件夹();
            目录 = 目录 + "\\" + 名称 + 临时计数器++.ToString() + ".bmp";
            bmp.Save(目录, ImageFormat.Bmp);
        }
    }
}
